class Wrap<T> {
    public timer?: number;
    constructor(public data: T, public idle: boolean, public owner: Pool<T>) {}

    /** 被调用时触发的函数 */
    awake() {
        this.idle = false;
        // 如果有自动删除函数的话，先删除自动删除函数
        if (typeof this.timer === "number") {
            clearTimeout(this.timer);
            this.timer = undefined;
        }
    }

    /** 被放回时触发的函数 */
    sleep() {
        setTimeout(() => {
            this.idle = true;
        }, this.owner.coolTime);

        // 设置自动删除函数
        if (
            this.owner.keepNumber < this.owner.all.size &&
            this.owner.autoClearTime !== Infinity
        ) {
            this.timer = setTimeout(() => {
                this.owner.destroyChild(this);
            }, this.owner.autoClearTime);
        }
    }
    destroy() {
        this.owner = undefined as any;
        this.data = undefined as any;
    }
}
export class Pool<T> {
    all = new Set<Wrap<T>>();

    constructor(
        private require: () => T,
        public _release: (item: T) => void,
        public autoClearTime = Infinity,
        public keepNumber = 0,
        public coolTime = 0
    ) {}

    use<B>(cb: (input: T) => Promise<B>): Promise<B> {
        return new Promise<B>((resolve) => {
            let idleEl: Wrap<T> | undefined;
            // 优先进行线程申请
            if (this.all.size >= this.keepNumber) {
                for (const iterator of this.all.values()) {
                    if (iterator.idle) {
                        idleEl = iterator;
                        break;
                    }
                }
            }
            if (!idleEl) {
                const wrap = new Wrap(this.require(), false, this);
                this.all.add(wrap);
                idleEl = wrap;
            }
            idleEl.awake();
            cb(idleEl.data).then((data) => {
                idleEl!.sleep();
                resolve(data);
            });
        });
    }
    destroyChild(wrap: Wrap<T>) {
        this.all.delete(wrap);
        this._release(wrap.data);
        wrap.destroy();
    }
}
